/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import { createHash } from 'crypto';
import * as path from 'path';

import { cloneDeep } from '@baize/wdk';
import { FileSet, replaceBundleName, TaskInputValue, hvigorCore } from '@ohos/hvigor';
import * as fse from 'fs-extra';

import { BuildArtifactConst, BuildDirConst } from '../../const/build-directory-const.js';
import { InjectConst } from '../../const/inject-const.js';
import { AbilityTypeEnum } from '../../enum/ability-type-enum.js';
import { LegacyAbilityModel } from '../../model/ability/legacy-ability-model.js';
import { LegacyModuleModelImpl } from '../../model/module/legacy-module-model-impl.js';
import { ModuleBuildProfile } from '../../options/build/module-build-profile.js';
import { ConfigJson } from '../../options/configure/config-json-options.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { ModuleTargetData } from '../data/hap-task-target-data.js';
import { TargetTaskService } from '../service/target-task-service.js';
import { OhosHapTask } from '../task/ohos-hap-task.js';
import ConfigOptObj = ConfigJson.ConfigOptObj;
import { TaskNames } from '../common/task-names.js';
import LegacyFATask = TaskNames.LegacyFATask;
import { LegacyPreBuild } from './legacy-pre-build.js';
import AbilityBuildOpt = ModuleBuildProfile.AbilityBuildOpt;
import { CommonConst } from '../../const/common-const.js';
import { InjectUtil } from '../../utils/inject-util.js';

const SRC_PATH = InjectConst.PREVIEWER_REPLACE_SRC_PATH;
const UNIT_TEST_SRC_PATH = InjectConst.UNIT_TEST_REPLACE_SRC_PATH;
const PAGE = InjectConst.PREVIEWER_REPLACE_PAGE;
const UNIT_TEST_PAGE = InjectConst.UNIT_TEST_REPLACE_PAGE;
const BUILD_ROOT = InjectConst.BUILD_ROOT;

/**
 * 遍历project的module信息并生成JsManifest.json文件
 *
 * @since 2022/1/19
 */
export class LegacyGenerateJsManifest extends OhosHapTask {
  private _log: OhosLogger = OhosLogger.getLogger(LegacyGenerateJsManifest.name);
  private readonly previewSrcPath: string | undefined;
  private readonly unitTestSrcPath: string | undefined;
  private readonly isPreview: boolean | undefined;
  private readonly previewPage: string | undefined;
  private readonly unitTestPage: string | undefined;
  private readonly isUnitTest: boolean;
  private readonly _moduleModel: LegacyModuleModelImpl;
  private readonly _abilityModels: LegacyAbilityModel[];
  private readonly _configOpt: ConfigOptObj;
  private readonly _minPlatformVersion: number;

  constructor(taskService: TargetTaskService) {
    super(taskService, LegacyFATask.GENERATE_JS_MANIFEST);
    this.previewSrcPath = hvigorCore.getExtraConfig().get(SRC_PATH);
    this.unitTestSrcPath = hvigorCore.getExtraConfig().get(UNIT_TEST_SRC_PATH);
    this.previewPage = hvigorCore.getExtraConfig().get(PAGE);
    this.unitTestPage = hvigorCore.getExtraConfig().get(UNIT_TEST_PAGE);
    this.isUnitTest = InjectUtil.isUnitTestProcess();
    this.isPreview = hvigorCore.getExtraConfig().get(BUILD_ROOT) === BuildDirConst.PREVIEW_BUILD_PATH;
    this._moduleModel = this.service.getModuleModel()! as LegacyModuleModelImpl;
    this._abilityModels = this._moduleModel.getLegacyAbilities(this.targetData.getTargetName());
    this._configOpt = cloneDeep(this._moduleModel.getJsonObjByTargetName(this.targetData.getTargetName()));
    this._minPlatformVersion = this.targetData.getCompatibleApiVersion();
  }

  initTaskDepends() {
    this.declareDepends(LegacyPreBuild.name);
  }

  declareInputs(): Map<string, TaskInputValue> {
    const inputs = new Map<string, TaskInputValue>();
    const abilities: AbilityBuildOpt[] | undefined = this.targetData.getTargetOpt().source?.abilities;
    const targetOptAbilities: string = JSON.stringify(abilities ? abilities : []);
    const abiHash: string = createHash('MD5').update(JSON.stringify(this._abilityModels)).digest('hex');
    const configHash: string = createHash('MD5').update(JSON.stringify(this._configOpt)).digest('hex');
    const abilitiesHash: string = createHash('MD5').update(targetOptAbilities).digest('hex');
    inputs.set('ABI_OBJ_HASH', abiHash);
    inputs.set('CONFIG_OBJ', configHash);
    inputs.set('ABILITIES_HASH', abilitiesHash);
    inputs.set('HAS_LITE_DEVICE', this.service.hasLiteDevice());
    inputs.set('MIN_PLATFORM_VERSION', this._minPlatformVersion);
    if (this.isPreview) {
      inputs.set('PREVIEWER_REPLACE_PAGE', JSON.stringify(hvigorCore.getExtraConfig().get(PAGE)));
      inputs.set('PREVIEWER_REPLACE_SRCPATH', JSON.stringify(hvigorCore.getExtraConfig().get(SRC_PATH)));
    }

    return inputs;
  }

  declareOutputFiles(): FileSet {
    return new FileSet().addEntry(this.targetData.getPathInfo().getIntermediatesLegacyManifestJson());
  }

  protected doTaskAction(): void {
    for (const abilityInfo of this._abilityModels) {
      if (abilityInfo.getType() !== AbilityTypeEnum.PAGE && abilityInfo.getType() !== AbilityTypeEnum.FORM) {
        continue;
      }
      this.generateJsonInfo(abilityInfo, this._configOpt, this.targetData);
    }
  }

  private getUnitTestManifestPage(targetData: ModuleTargetData, legacyAbilityInfo: LegacyAbilityModel): string[] {
    if (!this.unitTestPage) {
      let pages = this.getAbilityPages(targetData, legacyAbilityInfo);
      if (pages === undefined) {
        pages = legacyAbilityInfo.getConfigJsonJsObj()?.pages;
      }
      return pages ? pages : [];
    }
    this._log.debug(`replace ${this.unitTestPage} for ${this.unitTestSrcPath} manifest.`);
    return [this.unitTestPage];
  }

  private getManifestPages(targetData: ModuleTargetData, legacyAbilityInfo: LegacyAbilityModel): string[] | undefined {
    if (!this.isPreview || !this.previewPage || (this.previewSrcPath && this.previewSrcPath !== legacyAbilityInfo.getRelateSrcPath())) {
      // 非预览场景优先使用target中的配置
      let pages = this.getAbilityPages(targetData, legacyAbilityInfo);
      if (pages === undefined) {
        // target中没有定制, 则使用config.json中的
        pages = legacyAbilityInfo.getConfigJsonJsObj()?.pages;
      }
      return pages ? pages : [];
    }
    this._log.debug(`replace ${this.previewPage} for ${this.previewSrcPath} manifest.`);
    return [this.previewPage];
  }

  private getAbilityPages(targetData: ModuleTargetData, abilityObj: LegacyAbilityModel): string[] | undefined {
    const targetConfigOpt = targetData.findTargetConfigOpt(abilityObj);
    if (targetConfigOpt) {
      return targetConfigOpt.pages;
    }
    return undefined;
  }

  private generateJsonInfo(legacyAbilityInfo: LegacyAbilityModel, configJsonObj: ConfigJson.ConfigOptObj, targetData: ModuleTargetData) {
    let manifestObj: object = {
      appID: configJsonObj.app.bundleName,
      versionName: configJsonObj.app.version.name,
      versionCode: configJsonObj.app.version.code,
      minPlatformVersion: this._minPlatformVersion,
      appName: LegacyGenerateJsManifest.findAppName(configJsonObj),
      deviceType: configJsonObj.module.deviceType,
      window: this.getWindowObj(legacyAbilityInfo),
      pages: this.isUnitTest ? this.getUnitTestManifestPage(targetData, legacyAbilityInfo) : this.getManifestPages(targetData, legacyAbilityInfo),
    };

    const mode = legacyAbilityInfo.getConfigJsonJsObj()?.mode;
    const type = legacyAbilityInfo.getType() === AbilityTypeEnum.FORM ? AbilityTypeEnum.FORM : legacyAbilityInfo.getConfigJsonJsObj()?.type;
    if (mode) {
      manifestObj = { ...manifestObj, mode: mode };
    } else if (type) {
      manifestObj = { ...manifestObj, type: type };
    }

    this._log._printDebugCommand('JsManifest', replaceBundleName(manifestObj, configJsonObj.app.bundleName));
    const manifestFile = path.resolve(
      targetData.getPathInfo().getIntermediatesLegacyManifestJson(),
      legacyAbilityInfo.getRelateSrcPath(),
      BuildArtifactConst.LEGACY_MANIFEST_JSON,
    );
    fse.outputJSONSync(manifestFile, manifestObj);
  }

  private static findAppName(configJsonObj: ConfigOptObj): string {
    // find main ability
    const abilities: ConfigJson.AbilitiesObj[] = configJsonObj.module.abilities ?? [];
    for (const ability of abilities) {
      if (!ability.skills) {
        continue;
      }
      if (ability.skills[0]?.actions && ability.skills[0]?.actions[0] === 'action.system.home') {
        return ability.name;
      }
    }
    if (abilities[0].label) {
      return abilities[0].label;
    }
    return abilities[0].name;
  }

  private getWindowObj(legacyAbilityInfo: LegacyAbilityModel): object | undefined {
    if (this.targetData.isSingleDeviceTypeTarget()) {
      return this.service.hasLiteDevice() ? undefined : legacyAbilityInfo.getConfigJsonJsObj()?.window;
    } else {
      const window = legacyAbilityInfo.getConfigJsonJsObj()?.window;
      return (
        window ?? {
          autoDesignWidth: false,
          designWidth: CommonConst.DEFAULT_DESIGN_WIDTH,
        }
      );
    }
  }
}
